<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ownKeys</title>
  </head>
  <body>
    <script>
      // ownKeys() 方法用来拦截对象自身属性的读取操作！！注意是自身属性！！具体来说拦截以下操作
      /**
       * 1. Object.getOwnPropertyNames()
       * 2. Object.getOwnPropertySymbols()
       * 3. Object.keys()
       * 4. for...in循环
       */
      // 下面是拦截Object.keys()的例子。
      var target = {
        a: 1,
        b: 2,
        c: 3,
      };
      var handler = {
        ownKeys(target) {
          return ["a"];
        },
      };
      var proxy = new Proxy(target, handler);
      var arr = Object.keys(proxy);
      console.log("arr", arr); // ['a']
    </script>
    <script>
      // 下面的例子是拦截第一个字符为下划线的属性名
      var target = {
        _bar: "foo",
        _prop: "bar",
        prop: "baz",
      };
      var handler = {
        ownKeys(target) {
          return Reflect.ownKeys(target).filter((key) => key[0] !== "_");
        },
      };
      var proxy = new Proxy(target, handler);
      var b = Object.keys(proxy);
      console.log("b", b); // b ['prop']
      for (let key of Object.keys(proxy)) {
        console.log(target[key]); // baz
      }
      // 注意，使用Object.keys()方法时，有三类属性会被ownKeys()方法自动过滤，不会返回
      /**
       * 1. 目标对象上不存在的属性
       * 2. 属性名为symbol值
       * 3. 不可遍历(enumerable)
       */
      var target = {
        a: 1,
        b: 2,
        c: 3,
        [Symbol.for("secret")]: "4",
      };
      Object.defineProperty(target, "key", {
        enumerable: false,
        configurable: true,
        writable: true,
        value: "static",
      });
      var handler = {
        ownKeys(target) {
          return ["a", "d", Symbol.for("secret"), "key"];
        },
      };
      var proxy = new Proxy(target, handler);
      var c = Object.keys(proxy);
      console.log("c", c); // c ['a']
      // 虽然在拦截器里定义了返回的数据，但是只返回了 'a' 其余的都被过滤掉了
      // 'd' 不存在， Symbol值被过滤， 'key' 不可遍历
    </script>
    <script>
      // ownKeys方法还可以拦截Object.getOwnPropertyNames()
      var p = new Proxy(
        {},
        {
          ownKeys: function(target) {
            return ["a", "b", "c"];
          },
        }
      );
      // 目标对象是空对象
      Object.getOwnPropertyNames(p);
      // [ 'a', 'b', 'c' ]
    </script>
    <script>
      // for ...in 循环也受到ownKeys方法拦截
      var obj = { hello: "world" };
      var proxy = new Proxy(obj, {
        ownKeys: function() {
          return ["a", "b"];
        },
      });

      for (let key in proxy) {
        console.log(key); // 没有任何输出
      }
      //上面代码中，ownkeys()指定只返回a和b属性，由于obj没有这两个属性，因此for...in循环不会有任何输出。
      // ownKeys()方法返回的数组成员，只能是字符串或 Symbol 值。如果有其他类型的值，或者返回的根本不是数组，就会报错。
      var obj = {};

      var p = new Proxy(obj, {
        ownKeys: function(target) {
          return [123, true, undefined, null, {}, []];
        },
      });

      Object.getOwnPropertyNames(p);
      // Uncaught TypeError: 123 is not a valid property name
      // 上面代码中，ownKeys()方法虽然返回一个数组，但是每一个数组成员都不是字符串或 Symbol 值，因此就报错了。

      // 如果目标对象自身包含不可配置的属性，则该属性必须被ownKeys()方法返回，否则报错。
      var obj = {};
      Object.defineProperty(obj, "a", {
        configurable: false,
        enumerable: true,
        value: 10,
      });

      var p = new Proxy(obj, {
        ownKeys: function(target) {
          return ["b"];
        },
      });

      Object.getOwnPropertyNames(p);
      // Uncaught TypeError: 'ownKeys' on proxy: trap result did not include 'a'
      // 上面代码中，obj对象的a属性是不可配置的，这时ownKeys()方法返回的数组之中，必须包含a，否则会报错

      //另外，如果目标对象是不可扩展的（non-extensible），这时ownKeys()方法返回的数组之中，必须包含原对象的所有属性，且不能包含多余的属性，否则报错。
      var obj = {
        a: 1,
      };

      Object.preventExtensions(obj);

      var p = new Proxy(obj, {
        ownKeys: function(target) {
          return ["a", "b"];
        },
      });

      Object.getOwnPropertyNames(p);
      // Uncaught TypeError: 'ownKeys' on proxy: trap returned extra keys but pr
      // 上面代码中，obj对象是不可扩展的，这时ownKeys()方法返回的数组之中，包含了obj对象的多余属性b，所以导致了报错。
    </script>
  </body>
</html>
